class sim2dLexer extends Lexer;
options {
	k = 2;
   charVocabulary = '\3'..'\377';
   testLiterals = false;
} 
 
DIV     options { testLiterals = true; } : '/' ;
STAR    options { testLiterals = true; } : '*' ;
PLUS    options { testLiterals = true; } : '+' ;
MINUS   options { testLiterals = true; } : '-' ;
ASSIGN  options { testLiterals = true; } : "<-" ;
EQ : '=';
NEQ : "!=";
LT : '<';
GT : '>';
LTEQ : "<=";
GTEQ : ">=";
COMMA : ',';
SEMI  : ';' ;
DEREF : '.' ;

PARENS options { testLiterals = true; } : '(' | ')' ;
BRACES options { testLiterals = true; } : '{' | '}' ;


ID options {testLiterals = true;} : ('a'..'z' | 'A'..'Z')
	('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
	;


NUMBER  : (DIGIT)+ ('.' (DIGIT)*)? (('E'|'e') ('+'|'-')? (DIGIT)+)? 
	;


NL      : ('\n' | ('\r' '\n') => '\r' '\n' | '\r') 
            { $setType(Token.SKIP); newline(); }
        ;
COMMENT : ( "/*" ( 
                    options {greedy=false;} : 
                    (NL)
                    | ~( '\n' | '\r' )
                 )* "*/"
            | "//" (~( '\n' | '\r' ))* (NL)
          )                      { $setType(Token.SKIP); }
        ;

STRING  : '"'!
              (  ~('"' | '\n')
               | ('"'!'"')
              )*
          '"'!
        ;

protected EXP : 'e' ('+' | '-')? ( '0' .. '9')+
	;
protected DIGIT : ( '0' .. '9' )
	;

WS : (' ' | '\t' ) //Whitespace
{ $setType(Token.SKIP); } ;// Action: ignore

class sim2dParser extends Parser;
options {
	k = 2;
   buildAST = true;
}

tokens {
  STATEMENTS;
  FUNCTION;
  ARGS;
  FUNC_DEF_ARGS;
  UPLUS;
  UMINUS;
}

file : ( object_stmt | func_def | rule_stmt )+ EOF!
        { #file = #([STATEMENTS,"FILE"],file); }
    ; 

any_stmt : object_stmt 
         | if_stmt
         | assign_stmt
         | return_stmt
         | function_stmt
		   ;

object_stmt : "object"^ ID 
              "{"! 
              (assign_stmt)*
              "}"!
				  ;

seq_of_stmts : ( any_stmt )*
			   {#seq_of_stmts = #([STATEMENTS,"SEQ_OF_STMTS"], seq_of_stmts); }
				;

rule_stmt : "rule"^ ID
            "{"! 
				( seq_of_stmts )
            "}"!
			   ;

if_stmt : "if"^ expr 
            "{"! 
				( seq_of_stmts )
            "}"!
				(options {greedy = true;}: 
				    "else"! 
                "{"! 
					 ( assign_stmt | if_stmt | function_stmt )* 
                "}"!
            )?
			   ;

assign_stmt : id "<-"^ expr SEMI!
              ;

func_def : "func"^ id func_def_args
            "{"! 
				( seq_of_stmts )
            "}"!
			   ;

return_stmt : "return"^ (expr)? SEMI!
	;

func_def_args : "("! (farg_list)? ")"!
  { #func_def_args = #([FUNC_DEF_ARGS], func_def_args); }
	;

function : ID args
            {#function = #([FUNCTION], function); };

function_stmt : function SEMI!;

args : "("! (arg_list)? ")"!
  { #args = #([ARGS], args); }
            ;

farg_list : ID ( COMMA! ID )*
         ;
arg_list : expr ( COMMA! expr )*
         ;


id : ID ( DEREF^ ID )*; 

expr : and_expr ( "or"^ and_expr)*;
and_expr : comp_expr ( "and"^ comp_expr)*;
comp_expr : plus_minus_expr ( ( EQ^ | NEQ^ | LT^ | GT^ | LTEQ^ | GTEQ^ ) plus_minus_expr )?;
plus_minus_expr : mult_div_expr (( "+"^ | "-"^) mult_div_expr)* ;
mult_div_expr : sign_expr (("*"^ | "/"^) sign_expr)* ;
sign_expr : "+"! deref_expr 
              {#sign_expr = #([UPLUS,"UPLUS"], sign_expr); } 
          | "-"! deref_expr 
              {#sign_expr = #([UMINUS,"UMINUS"], sign_expr); }
          | deref_expr ;
deref_expr : atom ( "."^ atom )*; 
atom : NUMBER | STRING | "true" | "false" | id | function | "("! expr ")"! ;




{
import java.util.Vector;
}

class sim2dWalker extends TreeParser;

{
    static Sim2dDataType null_data = new Sim2dDataType( "<NULL>" );
}

assign_stmt_in_rule returns [Sim2dDataType r]
{ Sim2dDataType a,b; r=null_data;
  Sim2dInterpreter interp = Sim2dInterpreter.getInstance(); }
	: #("<-" a=expr b=expr) 
			{ 
				r=b; 
				interp.assign(a, b);
				log.out("walker assign_stmt_in_rule, " 
						+ a.getName() + " = " + b.getName());
			}
	;

assign_stmt_in_obj_decl returns [Sim2dDataType r]
{ Sim2dDataType a,b; r=null_data; 
  Sim2dInterpreter interp = Sim2dInterpreter.getInstance(); }
	: #("<-" a=expr b=expr) 
			{ 
				r=b; 
				interp.put_into_obj(a, b);
				log.out("walker assign_stmt_in_obj_decl, " 
						+ a.getName() + " = " + b.getName());
			}
	;

expr returns [Sim2dDataType r]
{ Sim2dDataType a,b; r=null_data; 
  Sim2dDataType[] bs;
  Sim2dInterpreter interp = Sim2dInterpreter.getInstance(); }
   :  #(STATEMENTS (r=expr)* 
		 )
	 | #("-" a=expr b=expr)  { r = a.subtract(b); }
	 | #("+" a=expr b=expr)  { r = a.add(b); }
	 | #("*" a=expr b=expr)  { r = a.multiply(b); }
	 | #("/" a=expr b=expr)  { r = a.lfracts(b); }
	 | #(GTEQ a=expr b=expr) { r = a.gteq( b ); }
	 | #(LTEQ a=expr b=expr) { r = a.lteq( b ); }
	 | #(GT a=expr b=expr)   { r = a.gt( b ); }
	 | #(LT a=expr b=expr)   { r = a.lt( b ); }
	 | #(EQ a=expr b=expr)   { r = a.eq( b ); }
	 | #(NEQ a=expr b=expr)  { r = a.neq( b ); }
	 | #(UPLUS a=expr )      { r = a; }
	 | #(UMINUS a=expr )     { r = a.uminus( ); }
	 | #("or" a=expr right_or:.)
			{ 
				if ( a instanceof Sim2dBool )
					r = ( ((Sim2dBool)a).var ? a : expr(#right_or) );
				else
					r = a.or( expr(#right_or) );
			}
	 | #("and" a=expr right_and:.) 
			{
				if ( a instanceof Sim2dBool )
					r = ( ((Sim2dBool)a).var ? expr(#right_and) : a );
				else
					r = a.and( expr(#right_and) );
			}
	 | #("not" a=expr)            { r = a.not(); }

	 | #("<-" a=expr b=expr)
			{ 
				r=b; 
				interp.assign(a, b);
				log.out("walker expr " 
						+ a 
						+ " <- "
						+ b);
			}
    | #(DEREF a=expr b=expr
	      {
				Sim2dObject o = (Sim2dObject)interp.getSymbol(a.getName());
				r = o.getField(b.getName());
			}
       )
    | #(fu:FUNCTION a=expr bs=arguments
		 {
			 Sim2dDataType d = interp.getSymbol(a.getName() ); 
			 if (!(d instanceof Sim2dFunction)) {
				 throw new Sim2dException("Undefined function " + a.getName());
		    }
			 Sim2dFunction f = (Sim2dFunction)d;
		    log.out("got function call " + a.getName());
			 interp.enterScope();
			 r = f.execute(interp.sym, bs);
			 interp.leaveScope();
		 }
		 )
    | #("if" a=expr ifbody:. (elsebody:.)?)
         {
            if ( !( a instanceof Sim2dBool ) )
                return a.error( "if: expression should be bool" );
            if ( ((Sim2dBool)a).var )
                r = expr( #ifbody );
            else if ( null != elsebody )
                r = expr( #elsebody );
         }

	 | "true"  { r = new Sim2dBool(true);  }
	 | "false" { r = new Sim2dBool(false); }

    | id:ID 
			{ 
				r = interp.getSymbol(id.getText() ); 
			} 
    | num:NUMBER            
			{ 
				r = interp.getNumber( num.getText() ); 
			}
	;

arguments returns [ Sim2dDataType[] rargs ]
{
    Sim2dInterpreter interp = Sim2dInterpreter.getInstance();
    Sim2dDataType a;
    rargs = null;
    Vector v;
}
        : #(ARGS          { v = new Vector(); } 
                ( a=expr      { v.add( a ); } 
                )* 
           )                  { rargs = interp.convertExprList( v ); }
        ;

file returns [ Sim2dDataType r ]
{ Sim2dDataType a; r=null_data; }
	:  #(STATEMENTS (a=object_or_rule_stmt)* 
			{ 
			   log.out("walker: FILE ");  
			} 
		 )
   ;

object_or_rule_stmt returns [ Sim2dDataType r ]
{ Sim2dDataType a; r=null_data; 
  String[] fargs;
  Sim2dInterpreter interp = Sim2dInterpreter.getInstance();}
   :  #("object" oname:ID 
			{   
			   log.out("object " + oname.getText()); 
				Sim2dObject newobj = new Sim2dObject(oname.getText());
				interp.put(newobj); 
				interp.setObjectDefinitionMode(true);
			} 
			(a=assign_stmt_in_obj_decl)*
			{ 
			   interp.objectPostProcessing();// add fields that all objects have
				interp.setObjectDefinitionMode(false);
				log.out("object def complete");
			}
	 	 )
    | #("func" fname:ID fargs=tlist funcbody:.)
		   { 
			   log.out("func " + fname.getText()); 
				interp.put(new Sim2dUserFunction(interp.walker, fname.getText(), 
                                             fargs, #funcbody )); 
				
			} 
    | #("rule" rname:ID rulebody:.)
		   { 
			   log.out("rule " + rname.getText()); 
				interp.put(new Sim2dRule(rname.getText(), #rulebody )); 
				
			} 
		 
	;

tlist returns [ String[] fargs ]
{
    Vector v;
    fargs = null;
}
        : #(FUNC_DEF_ARGS { v = new Vector(); }
              (s:ID       { v.add( s.getText() ); }
              )*
           )              { fargs = new String[v.size()];
			                   v.toArray(fargs); }
        ;

